#ifndef __TSERIALIZE_h__
#define __TSERIALIZE_h__
#include <string>

static bool is_big_endian()
{
	static unsigned short test_number = 0x1234;
	static bool fIsBigEndian = ( *( (char*)(&test_number) ) == 0x12 );
	return fIsBigEndian;
}

static int limit_max(int input, int max)
{
	return ( input > max && max > 0 )?max:input;
}

template<class T>
T exchange_value(T val)
{
	T retval = val;
	if( !is_big_endian() )
	{
		int ibytes = (int) sizeof( T );
		for( int i = 0; i < ibytes; i++ )
		{
			*( ( (char*)&retval )+i ) = *( ( (char*)&val ) + ibytes - 1 - i );
		}
	}
	return retval;
}

class TSerialize
{
public:
	typedef enum{PACK=0, UNPACK} PACK_CODE;
	static int		DEFAULT_PACK_SIZE;
public:
	TSerialize( PACK_CODE code, int cbmax = DEFAULT_PACK_SIZE )
	{
		m_ctlCode = code;
		m_pdata = 0;
		m_cbdata = 0;
		m_offset = 0;
		allocbuffer( cbmax );
	}
	virtual ~TSerialize()
	{
		destroy();
	}
private:
	void destroy()
	{
		free( m_pdata );
		m_pdata = 0;
		m_cbdata = 0;
		m_offset = 0;
	}

protected:
	TSerialize& operator<<(__int8 ch1)
	{
		pack_data( &ch1, 1 );
		return *this;
	}
	TSerialize& operator<<(__int16 ch2)
	{
		__int16 val = exchange_value<__int16>(ch2);
		pack_data( (char*)&val, 2 );
		return *this;
	}
	TSerialize& operator<<(__int32 ch4)
	{
		__int32 val = exchange_value<__int32>(ch4);
		pack_data( (char*)&val, 4 );
		return *this;
	}
	TSerialize& operator<<(__int64 ch8)
	{
		__int64 val = exchange_value<__int64>(ch8);
		pack_data( (char*)&val, 8 );
		return *this;
	}
	TSerialize& operator<<(bool b)
	{
		__int8 ch1 = b?1:0;
		(*this)<<ch1;
		return *this;
	}
	TSerialize& operator<<(float f)
	{
		__int32 ch4 = 0;
		memcpy(&ch4, &f, 4);
		(*this)<<ch4;
		return *this;
	}
	TSerialize& operator<<(double dbl)
	{
		__int64 ch8 = 0;
		memcpy(&ch8, &dbl, 8);
		(*this)<<ch8;
		return *this;
	}

	TSerialize& operator>>(__int8& ch1)
	{
		unpack_data( &ch1, 1 );
		return *this;
	}
	TSerialize& operator>>(__int16& ch2)
	{
		__int16 val = 0;
		unpack_data( (char*)&val, 2 );
		ch2 = exchange_value<__int16>(val);
		return *this;
	}
	TSerialize& operator>>(__int32& ch4)
	{
		__int32 val = 0;
		unpack_data( (char*)&val, 4 );
		ch4 = exchange_value<__int32>(val);
		return *this;
	}
	TSerialize& operator>>(__int64& ch8)
	{
		__int64 val = 0;
		unpack_data( (char*)&val, 8 );
		ch8 = exchange_value<__int64>(val);
		return *this;
	}
	TSerialize& operator>>(bool& b)
	{
		__int8 ch1 = 0;
		(*this)>>ch1;
		b = ( ch1 != 0 );
		return *this;
	}
	TSerialize& operator>>(float& f)
	{
		__int32 ch4 = 0;
		(*this)>>ch4;
		memcpy(&f, &ch4, 4);
		return *this;
	}
	TSerialize& operator>>(double& dbl)
	{
		__int64 ch8 = 0;
		(*this)>>ch8;
		memcpy(&dbl, &ch8, 8);
		return *this;
	}

	void pack_data( const char* pdata, int cbdata )
	{
		if( m_ctlCode == UNPACK || pdata == 0 || cbdata <=0 )
			throw "invalid pack data parameters!";
		if( ( m_cbdata+cbdata ) > m_cbmax )
		{
			allocbuffer(m_cbmax+DEFAULT_PACK_SIZE);
			if( m_pdata == NULL )
				throw "not enough memory to cache the data!";
		}
		memcpy( m_pdata+m_offset, pdata, cbdata );
		seek(m_offset+cbdata);
		m_cbdata = m_offset;
	}
	void unpack_data( char* pdata, int cbdata )
	{
		if( m_ctlCode == PACK || pdata == 0 || cbdata <=0 )
			throw "invalid unpack data parameters!";
		if( ( m_offset+cbdata ) > m_cbmax )
			throw "not enough data to be retrieved!";
		memcpy( pdata, m_pdata+m_offset, cbdata );
		seek(m_offset+cbdata);
	}

	bool ispacking()
	{
		return ( m_ctlCode == PACK ) ;
	}

public:
	bool allocbuffer( int size )
	{
		if( m_pdata == 0 )
		{
			m_pdata = (char*) malloc ( size );
			m_cbmax = size;
		}
		else if( size > m_cbmax )
		{
			m_pdata = (char*) realloc ( m_pdata, size );
			m_cbmax = size;
		}
		return ( m_pdata != 0 );
	}

	bool copydata( char* pdata, int cbdata )
	{
		if( allocbuffer( cbdata ) && ( pdata != 0 ) )
		{
			return serial( pdata, cbdata );
		}
		return false;
	}

	template<class T>
	bool serial( T& val )
	{
		try
		{
			if( ispacking() )
			{
				(*this)<<val;
			}
			else
			{
				(*this)>>val;
			}
			return true;
		}
		catch(char* strerr)
		{
			printf( "%s\n", strerr );
		}
		return false;
	}

	bool serial( char* pdata, int size )
	{
		try
		{
			if( ispacking() )
			{
				pack_data( pdata, size );
			}
			else
			{
				unpack_data( pdata, size );
			}
			return true;
		}
		catch(char* strerr)
		{
			printf( "%s\n", strerr );
		}
		return false;
	}

	bool serial( std::string& str, int maxsize = -1 )
	{
		try
		{
			if( ispacking() )
			{
				int ilen = limit_max( str.length(), maxsize );
				(*this) << ilen;
				if( ilen > 0 )
					serial( (char*)str.c_str(), ilen );
			}
			else
			{
				int ilen = 0;
				str.empty();
				(*this) >> ilen;
				ilen = limit_max( ilen, maxsize );
				if( ilen > 0 )
				{
					char* pdata = (char*) malloc ( ilen+1 );
					*( pdata + ilen ) = 0;
					serial( pdata, ilen );
					str.assign(pdata, ilen+1);
				}
			}
			return true;
		}
		catch(char* strerr)
		{
			printf( "%s\n", strerr );
		}
		return false;
	}

	bool serial( std::wstring& str, int maxsize = -1 )
	{
		try
		{
			if( ispacking() )
			{
				int ilen = limit_max( str.length(), maxsize ) * 2;
				(*this) << ilen;
				if( ilen > 0 )
					serial( (char*)str.c_str(), ilen );
			}
			else
			{
				int ilen = 0;
				str.empty();
				(*this) >> ilen;
				ilen = limit_max( ilen, maxsize );
				if( ilen > 0 )
				{
					char* pdata = (char*) malloc ( ilen+2 );
					*( pdata + ilen ) = 0;
					*( pdata + ilen + 1 ) = 0;
					serial( pdata, ilen );
					str.assign((wchar_t*)pdata, ilen/2);
				}
			}
			return true;
		}
		catch(char* strerr)
		{
			printf( "%s\n", strerr );
		}
		return false;
	}

	void reset(PACK_CODE code)
	{
		m_ctlCode = code;
		m_offset = 0;
	}
	bool seek(int offset)
	{
		if( ( ispacking() && offset > m_cbmax )
			|| ( !ispacking() &&  offset > m_cbdata ) )
			return false;

		m_offset = offset;
		return true;
	}
	char* data() const
	{
		return m_pdata;
	}
	char* wdata()
	{
		return m_pdata;
	}
	int size() const
	{
		return m_cbdata;
	}
private:
	PACK_CODE	m_ctlCode;
	char*			m_pdata;
	int				m_cbdata;
	int				m_offset;
	int				m_cbmax;
};

int TSerialize::DEFAULT_PACK_SIZE = 81920;

#endif
